home *** CD-ROM | disk | FTP | other *** search
- #include <graphics libraries.h>
- #include <graphics toolbox.h>
- #include <qd library.h>
- #include <offscreen library.h>
- #include "TileConstants.h"
- #include "TileProtos.h"
-
- #include "Group1.h"
- #include "Group2.h"
-
- //---------------------------------------
- // Tile globals
- gxPoint gOrigin = {ff(0), ff(0)};
- gxColorSet gColors;
- offscreen gOffScreen;
- gxShape gTileShape, // The unit cell (transforms of gDragger contours)
- gGridShape, // A rect filled with the pattern
- gBGShape; // A rect filled with the background color
-
- gxShape gOpShapes[kNumOpShapes]; // Group of shapes to show the symmetry
- // operations
-
- dragger gDragger; // A record containing a manipulable contour & associated stuff
- gxPatternRecord gPattern;
-
- Boolean gContRedraw = false, gKeepClosed = true; // State Booleans
- long gSymmetry = p1; // The currently chosen symmetry group
- gxViewPort gViewPort;
-
- // ========= Prototypes ==========================================
-
- // Prototype of a function in utils.c we use
- long labs(long i);
-
- // Prototypes for routines private to this file
- long AddEdgePoint(gxPoint *clickPt, long prevIndex);
- Boolean RemoveEdgePoint(long index);
- Boolean IsConstrainedPoint(long ptIndex);
-
- void RemakeOpShapes(Boolean useDefaults, long newSymmetry);
-
- void TrackDraggerPoint(long dragPtIndex, gxPoint *startPt);
- Boolean OpHit(gxPoint *clickPt);
-
- void DrawDraggable(void);
- void EraseDraggable(void);
- void DrawOpShapes(void);
-
- void SetLinePoints(gxShape theLine, gxPoint *start, gxPoint *end);
-
- //===================================================================
-
- // Adds a point to the global snake
- long AddEdgePoint(gxPoint *clickPt, long prevIndex)
- {
- gxPoint newPt[] = {1L, 1L, ff(0), ff(0)};
-
- // put the new point in newPt poly and add it to the snake
- newPt[1] = *clickPt;
- GXSetPolygonParts(gDragger.snake1, prevIndex + 1, 0,
- (gxPolygons *)newPt, gxBreakNeitherEdit);
-
- switch(gSymmetry)
- {
- case p1:
- p1_AddedDragPoint(prevIndex);
- break;
-
- case pg:
- pg_AddedDragPoint(prevIndex);
- break;
-
- case pm:
- break;
-
- case cm:
- break;
-
- case p2:
- break;
-
- default:
- break;
- }
- return(prevIndex + 1);
- }
-
- // Removes a point
- Boolean RemoveEdgePoint(long index)
- {
- Boolean rslt = false;
-
- // If there are only 2 points, or if constraints are active and
- // it's a constrained point, then don't remove it
- if( (GXCountShapePoints(gDragger.snake1, 0) < 3) || IsConstrainedPoint(index) ||
- index == 1 ) // BUG: if index is one, GXSetPolygonParts deletes the entire contour
- SysBeep(10);
- else // Remove the point
- {
- long geo[25];
-
- GXSetPolygonParts(gDragger.snake1, index, 1, gxSetToNil, gxBreakNeitherEdit);
-
- switch(gSymmetry)
- {
- case p1:
- p1_RemovedDragPoint(index);
- break;
-
- case pg:
- pg_RemovedDragPoint(index);
- break;
-
- case pm:
- break;
-
- case cm:
- break;
-
- case p2:
- break;
-
- default:
- break;
- }
- rslt = true;
- }
- return rslt;
- }
-
- Boolean IsConstrainedPoint(long ptIndex)
- {
- Boolean isConstrained = false;
-
- if(gKeepClosed)
- {
- switch(gSymmetry)
- {
- case p1:
- isConstrained = p1_IsVectorPoint(ptIndex);
- break;
-
- case pg:
- isConstrained = pg_IsVectorPoint(ptIndex);
- break;
-
- case pm:
- break;
-
- case cm:
- break;
-
- case p2:
- break;
-
- default:
- break;
- }
- }
- return isConstrained;
- }
-
- // returns true if something changed: the shell will update the window
- Boolean TileClick(gxPoint *clickPt, short optDown)
- {
- gxHitTestInfo hitStats;
- gxTransform tileXForm;
- long dragPtIndex = -1;
- gxShape dragShape;
-
- // Check for a dragger hit
- tileXForm = GXGetShapeTransform(gDragger.snake1);
- GXIgnoreGraphicsNotice(attributes_already_set);
- GXSetTransformHitTest(tileXForm, gxGeometryPart, kHitTolerance);
- GXPopGraphicsNotice();
-
- // If snake was hit, do further tests to find out what
- if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
- {
- // check for hit on a control point
- GXSetTransformHitTest(tileXForm, gxCornerPointPart, kHitTolerance);
- if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
- {
- // if the option key is down, remove the point
- // and change the pattern
- if(optDown)
- {
- if(RemoveEdgePoint(hitStats.index))
- RemakeTile();
- }
-
- // else drag the point if it's not constrained
- else
- {
- if(IsConstrainedPoint(hitStats.index))
- SysBeep(10);
- else
- {
- dragShape = gDragger.snake1;
- dragPtIndex = hitStats.index;
- }
- }
- }
- else // Not a control point, prob'ly an edge
- {
- // check for hit on an edge: if so need to add a point
- GXSetTransformHitTest(tileXForm, gxEdgePart, kHitTolerance);
- if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
- {
- dragShape = gDragger.snake1;
- dragPtIndex = AddEdgePoint(clickPt, hitStats.index);
- }
- }
-
- // If a draggable hit, drag it
- if(dragPtIndex > 0)
- TrackDraggerPoint(dragPtIndex, clickPt);
- return true;
- }
-
- // Not a dragger hit, try the op shapes
- return OpHit(clickPt);
- }
-
- void TrackDraggerPoint(long dragPtIndex, gxPoint *startPt)
- {
- gxPoint pt, lastPt = *startPt;
-
- // Get current mouse
- GXGetViewPortMouse(gViewPort, &pt);
-
- // Follow the drag around
- while(StillDown())
- {
- GXGetViewPortMouse(gViewPort, &pt);
- if(pt.x != lastPt.x || pt.y != lastPt.y)
- {
- // Erase
- if(!gContRedraw)
- EraseDraggable();
-
- // Set new point
- GXSetShapePoints(gDragger.snake1, dragPtIndex, 1, &pt);
-
- if(gContRedraw)
- {
- // Reset pattern and draw it right now
- RemakeTile();
- GXDrawShape(gOffScreen.draw);
- }
- else
- {
- // Just draw the dragger
- DrawDraggable();
- }
- }
- lastPt = pt;
- }
-
- // Update pattern if we haven't yet
- if(!gContRedraw)
- RemakeTile();
- }
-
- // returns true if something was hit so the shell will update the window
- Boolean OpHit(gxPoint *clickPt)
- {
- Boolean goodHit = false;
-
- switch(gSymmetry)
- {
- case p1:
- goodHit = p1_OpShapeHit(clickPt);
- break;
-
- case pg:
- goodHit = pg_OpShapeHit(clickPt);
- break;
-
- case pm:
- goodHit = pm_OpShapeHit(clickPt);
- break;
-
- case cm:
- goodHit = cm_OpShapeHit(clickPt);
- break;
-
- case p2:
- goodHit = p2_OpShapeHit(clickPt);
- break;
-
- default:
- goodHit = false;
- break;
- }
- return goodHit;
- }
-
- // Set up the default OpShapes and dragger for the symmetry
- void DefaultTileAndPattern()
- {
- RemakeOpShapes(true, gSymmetry);
-
- switch(gSymmetry)
- {
- case p1:
- p1_SetDefaultDragger(gDragger.snake1);
- p1_ChangeLattice();
- break;
-
- case pg:
- pg_SetDefaultDragger(gDragger.snake1);
- pg_ChangeLattice();
- break;
-
- case pm:
- pm_SetDefaultDragger(gDragger.snake1);
- pm_ChangeLattice();
- break;
-
- case cm:
- cm_SetDefaultDragger(gDragger.snake1);
- cm_ChangeLattice();
- break;
-
- case p2:
- p2_SetDefaultDragger(gDragger.snake1);
- p2_ChangeLattice();
- break;
-
- default:
- break;
- }
- if(gKeepClosed)
- AttachConstraints();
- }
-
- void AttachConstraints(void)
- {
- // Just call the right routine to do the work
- switch(gSymmetry)
- {
- case p1:
- p1_AttachConstraints();
- break;
-
- case pg:
- pg_AttachConstraints();
- break;
-
- case pm:
- break;
-
- case cm:
- break;
-
- case p2:
- break;
-
- default:
- break;
- }
- }
-
- // Called either to set the default shapes when resetting the tile, or when
- // the symmetry has changed, to convert one lattice type to another.
- // Currently always uses defaults.
- void RemakeOpShapes(Boolean useDefaults, long newSymmetry)
- {
- // Kill old shapes
- DisposeOpShapes();
-
- // Make the right new ones
- switch(newSymmetry)
- {
- case p1: // two translate shapes
- p1_RemakeOpShapes(useDefaults);
- break;
-
- case pg: // two glide lines
- pg_RemakeOpShapes(useDefaults);
- break;
-
- case pm: // two mirror lines
- pm_RemakeOpShapes(useDefaults);
- break;
-
- case cm: // a mirror and a glide
- cm_RemakeOpShapes(useDefaults);
- break;
-
- case p2: // 3 ovals
- p2_RemakeOpShapes(useDefaults);
- break;
-
- default:
- break;
- }
- }
-
- // Reset the tile shape according to the dragger
- void RemakeTile(void)
- {
- // Copy geometry into tile to start
- GXSetShapeGeometry(gTileShape, gDragger.snake1);
-
- // Repeat the geometry as needed for the symmetry
- switch(gSymmetry)
- {
- case p1: // No repeats
- break;
-
- case pg: // Glide reflect
- pg_BuildCellShape(gDragger.snake1);
- break;
-
- case pm:
- pm_BuildCellShape(gDragger.snake1);
- break;
-
- case cm:
- cm_BuildCellShape(gDragger.snake1);
- break;
-
- case p2:
- p2_BuildCellShape(gDragger.snake1);
- break;
-
- default:
- SysBeep(10);
- break;
- }
-
- // Set the new pattern
- GXIgnoreGraphicsNotice(pattern_already_set);
- GXSetShapePattern(gGridShape, &gPattern);
- GXPopGraphicsNotice();
-
- // Draw it offscreen
- DrawOff();
- } // RemakeTile
-
- // Change op shapes and dragger as necessary to fit new symmetry
- void ChangeSymmetry(long oldSym, long newSym)
- {
- RemakeOpShapes(false, newSym);
- gSymmetry = newSym;
- switch(gSymmetry)
- {
- case p1:
- p1_ChangeLattice();
- break;
-
- case pg:
- pg_ChangeLattice();
- break;
-
- case pm:
- pm_ChangeLattice();
- break;
-
- case cm:
- cm_ChangeLattice();
- break;
-
- case p2:
- p2_ChangeLattice();
- break;
-
- default:
- break;
- }
- if(gKeepClosed)
- AttachConstraints();
- }
-
-
-
- //-------------------------------------------------------------
- // Drawing routines
-
- // Draws everything
- void TileUpdate(void)
- {
- // Transfer grid from offscreen
- GXDrawShape(gOffScreen.draw);
-
- // Draw the ops shapes
- DrawOpShapes();
-
- // Draw the draggable bit
- DrawDraggable();
- }
-
- void DrawOff(void)
- {
- GXDrawShape(gBGShape);
- GXDrawShape(gGridShape);
- }
-
- void OffToScreen(void)
- {
- GXDrawShape(gOffScreen.draw);
- }
-
- void DrawDraggable(void)
- {
- GXDrawShape(gDragger.snake1);
- }
-
- void EraseDraggable(void)
- {
- EraseAShape(gDragger.snake1);
- }
-
- void DrawOpShapes(void)
- {
- short cnt = kNumOpShapes;
-
- while(--cnt >= 0)
- if(gOpShapes[cnt] != nil)
- GXDrawShape(gOpShapes[cnt]);
- }
-
- void EraseXShape(gxShape theShape);
-
- // Erases by drawing it in the BG color
- void EraseAShape(gxShape theShape)
- {
- commonColor savedColor;
-
- GXIgnoreGraphicsNotice(color_already_set);
-
- savedColor = GetShapeCommonColor(theShape);
- SetShapeCommonColor(theShape, kBGColor);
- GXDrawShape(theShape);
- SetShapeCommonColor(theShape, savedColor);
-
- GXPopGraphicsNotice();
- }
-
- // Erases by copying from the offscreen
- void EraseXShape(gxShape theShape)
- {
- gxShape savedClip, tempShape;
- gxRectangle shapeBounds;
-
- tempShape = GXCopyToShape(nil, theShape);
- GXPrimitiveShape(tempShape);
- GXGetShapeBounds(tempShape, 0, &shapeBounds);
- GXDisposeShape(tempShape);
- tempShape = GXNewRectangle(&shapeBounds);
- savedClip = GXGetShapeClip(gOffScreen.draw);
- GXSetShapeClip(gOffScreen.draw, tempShape);
- GXDisposeShape(tempShape);
- GXDrawShape(gOffScreen.draw);
- GXSetShapeClip(gOffScreen.draw, savedClip);
- GXDisposeShape(savedClip);
- }
-
- void DisposeDragger(void)
- {
- GXDisposeShape(gDragger.snake1);
- }
-
- void DisposeOpShapes(void)
- {
- short cnt = kNumOpShapes;
-
- while(--cnt >= 0)
- if(gOpShapes[cnt] != nil)
- {
- GXDisposeShape(gOpShapes[cnt]);
- gOpShapes[cnt] = nil;
- }
- }
-
- //-------------------------------------------------------------
- // From Luke
-
- gxShape GetWindowBoundsShape(WindowPtr wind)
- {
- Rect theRect;
- Point QDtopLeft;
- Point QDbotRight;
- gxPoint QD2topLeft;
- gxPoint QD2botRight;
- gxRectangle theQD2Rect;
-
- /** The QuickDraw rect and points which represent the portRect of the window. **/
- theRect = wind->portRect;
- QDtopLeft.h = theRect.left;
- QDtopLeft.v = theRect.top;
- QDbotRight.h = theRect.right;
- QDbotRight.v = theRect.bottom;
-
- /** Convert the global Quickdraw coordinates to local fixed coordinates. **/
- GXConvertQDPoint(&QDtopLeft, 0, &QD2topLeft);
- GXConvertQDPoint(&QDbotRight, 0, &QD2botRight);
-
- /** Setup the dimensions for "gBGShape" **/
- theQD2Rect.top = QD2topLeft.y;
- theQD2Rect.left = QD2topLeft.x;
- theQD2Rect.bottom = QD2botRight.y;
- theQD2Rect.right = QD2botRight.x;
-
- return (GXNewRectangle(&theQD2Rect));
- }
-
- Boolean ChangeWindowSize(WindowPtr wind, short hSize, short vSize)
- {
- gxBitmap offMap;
- gxShape offBits;
- gxRectangle windRect;
-
- // Try to remake the off-screen bitmap. !!! I'm sure there's a better way to do this,
- // just resizing the appropriate bitmap shape, whichever one it is.
- offMap.image = nil;
- offMap.width = hSize;
- offMap.height = vSize;
- offMap.rowBytes = 0;
- offMap.pixelSize = 8;
- offMap.space = gxIndexedSpace;
- offMap.set = gColors;
- offMap.profile = nil;
- offBits = GXNewBitmap(&offMap, &gOrigin);
- if(offBits <= (gxShape)nil)
- return false;
-
- // Kill old one and make new one
- DisposeOffscreen(&gOffScreen);
- CreateOffscreen(&gOffScreen, offBits);
- GXDisposeShape(offBits);
-
- // Size the window
- SizeWindow(wind, hSize, vSize, true);
- ClipRect(&wind->portRect);
-
- // Update other 2 rect shapes
- ShortRectToFixed(&wind->portRect, &windRect);
- GXSetRectangle(gBGShape, &windRect);
- GXSetRectangle(gGridShape, &windRect);
-
- // Set them to draw in new offscreen
- GXSetShapeTransform(gBGShape, gOffScreen.xform);
- GXSetShapeTransform(gGridShape, gOffScreen.xform);
-
- // Update the offscreen
- DrawOff();
-
- return true;
- }
-
- void SetLinePoints(gxShape theLine, gxPoint *start, gxPoint *end)
- {
- gxLine newGeo;
-
- newGeo.first = *start;
- newGeo.last = *end;
- GXSetLine(theLine, &newGeo);
- }